<?php 
namespace PsiFramework\Classes\Util\Parameters\DAO;

import("Classes.Util.Parameters.DAO.ParametersDAO");
import("Classes.Util.DAO.IPrimitiveObjectDAO");
import("Classes.Util.Database.Query.SqlColumn");
import("Classes.Util.Database.Query.SqlType");
import("Classes.Util.Database.Query.SqlTable");
import("Classes.Util.Database.Query.SqlCreateTableQuery");
import("Classes.Util.Database.Query.SqlCreateViewQuery");
import("Classes.Util.Database.Query.SqlDeleteTableQuery");
import("Classes.Util.Database.Query.SqlDeleteViewQuery");
import("Classes.Util.Database.Query.SqlCreateTriggerQuery");
import("Classes.Util.Parameters.ObjectParameters");
import("Classes.Util.Parameters.Object.AttributeParameters");
import("Classes.Util.Parameters.Object.Operations.DeleteOperation");
import("Classes.Util.Parameters.Object.Operations.InsertOperation");
import("Classes.Util.Parameters.Object.Operations.SelectOperation");
import("Classes.Util.Parameters.Object.Operations.UpdateOperation");
import("Classes.Util.Parameters.Object.Operations.Elements.OperationObject");

use \PsiFramework\Classes\Model\ModelInterface\IPrimitiveObject;
use \PsiFramework\Classes\Util\Manager\ApplicationManager;
use \PsiFramework\Classes\Util\Parameters\DAO\ParametersDAO;
use \PsiFramework\Classes\Util\DAO\IPrimitiveObjectDAO;
use \PsiFramework\Classes\Util\Database\SqlQuery;
use \PsiFramework\Classes\Util\Database\Builder\QueryBuilder;
use \PsiFramework\Classes\Util\Database\Query\SqlColumn;
use \PsiFramework\Classes\Util\Database\Query\SqlType;
use \PsiFramework\Classes\Util\Database\Query\SqlTable;
use \PsiFramework\Classes\Util\Database\Query\SqlJoin;
use \PsiFramework\Classes\Util\Database\Query\SqlOn;
use \PsiFramework\Classes\Util\Database\Query\SqlUsing;
use \PsiFramework\Classes\Util\Database\Query\SqlOperation;
use \PsiFramework\Classes\Util\Database\Query\SqlCreateTableQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlCreateViewQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlDeleteTableQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlDeleteViewQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlCreateTriggerQuery;
use \PsiFramework\Classes\Util\Database\Query\SqlPrimaryKey;
use \PsiFramework\Classes\Util\Database\Query\SqlForeignKey;
use \PsiFramework\Classes\Util\Database\Query\SqlValue;
use \PsiFramework\Classes\Util\Parameters\ObjectParameters;
use \PsiFramework\Classes\Util\Parameters\Object\AttributeParameters;
use \PsiFramework\Classes\Util\Parameters\Object\Operation;
use \PsiFramework\Classes\Util\Parameters\Object\Trigger;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\DeleteOperation;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\InsertOperation;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\SelectOperation;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\UpdateOperation;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\Elements\OperationClause;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\Elements\OperationColumn;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\Elements\OperationElement;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\Elements\OperationMultipleClause;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\Elements\OperationObject;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\Elements\OperationSimpleClause;
use \PsiFramework\Classes\Util\Parameters\Object\Operations\Elements\OperationValue;
use PsiFramework\Classes\Util\UI\Printer;

/**
 * PsiFramework
 * Php Simple Framework
 *
 * @author Rémi San
 * @version beta
 */

/**
 * The ParametersDAO class is the generic class to retrieve parameters
 *
 * @package Util.Parameters.DAO
 */
class ObjectParametersDAO extends ParametersDAO implements IPrimitiveObjectDAO
{

    protected static /*ObjectParametersDAO*/ $_singleton = null ;
    
    public static /*array*/ $objectParameters = array();

    public function parse($value=null, $options=null)
    {

    }

    public function edit(
        IPrimitiveObject $object=null,
        $name=null,
        $options=null
    )
    {

    }

    public function display(IPrimitiveObject $object=null, $options=null)
    {

    }

    public function javascript(IPrimitiveObject $object=null, $options=null)
    {

    }

    public static function readObject(\DOMElement $racine)
    {
        $parameters = array();

        $view = false ;

        $name = $racine->getAttribute("name");
        $class = $racine->getAttribute("class");
        $dao = $racine->getAttribute("dao");
        $table = $racine->getAttribute("table");
        if (isNullOrEmptyString($table)) {
            $table = $racine->getAttribute("view");
            $view = true ;
        }
        $schema = $racine->getAttribute("schema");
        $useJoin = ($racine->getAttribute("useJoin") == "true")?true:false ;
        
        $searchService = null;
        if (!isNullOrEmptyString($racine->getAttribute("search"))) {
            $searchService = $racine->getAttribute("search");
        }

        $attributes = $racine->getElementsByTagName("attribute");
        foreach ($attributes as $attribute) {
            $attArray = array();
            foreach ($attribute->attributes as $attName=>$attNode) {
                $attArray[$attName] = $attNode->nodeValue ;
            }
            $parameter= new AttributeParameters($attArray);
            $parameters[$parameter->name] = $parameter ;
        }

        $constructionQuery = null ;
        $constructionElements = $racine->getElementsByTagName("construction");

        if (!is_null($constructionElements)) {
            $constructionElement = $constructionElements->item(0);
            if (!is_null($constructionElement)) {
                $constructionQuery = self::getSelectOperation(
                    $constructionElement
                );
            }
        }

        $triggers = array();
        $triggersElements = $racine->getElementsByTagName("triggers");

        if (!is_null($triggersElements)) {
            $triggersElement = $triggersElements->item(0);

            $object = new OperationObject($name, 'superObject');

            if (!is_null($triggersElement)) {

                $triggerElements = $triggersElement->
                    getElementsByTagName("trigger");
                foreach ($triggerElements as $triggerElement) {

                    $trigger = self::getTrigger($triggerElement, $object);

                    if (!is_null($trigger)) {
                        if (!array_key_exists($trigger->getType(), $triggers)) {
                            $triggers[$trigger->getType()] = array();
                        }
                        $triggers[$trigger->getType()][] = $trigger ;
                    }
                }
            }
        }

        $objectParameters = new ObjectParameters(
            $name,
            $class,
            $dao,
            $table,
            $schema,
            $parameters,
            $useJoin,
            $searchService,
            $constructionQuery,
            $triggers
        );
        $objectParameters->setView($view);

        self::$objectParameters[$objectParameters->getName()]
            = $objectParameters ;

        return $objectParameters ;
    }

    /**
     *
     *
     * @see ParametersDAO::read()
     * @return Element
     */
    public function read(\DOMElement $racine)
    {
        return self::readObject($racine);
    }

    protected static function getTrigger(
        \DOMElement     $triggerElement,
        OperationObject $object
    )
    {

        $triggerName = $triggerElement->getAttribute("name");
        $triggerType = $triggerElement->getAttribute("type");
        $triggerMode = $triggerElement->getAttribute("mode");

        $trigger = new Trigger(
            $triggerName,
            $triggerType,
            $triggerMode,
            $object
        );

        $operationElements = $triggerElement->getElementsByTagName("action");
        if (!is_null($operationElements) && count($operationElements)>0) {
            $operationElement = $operationElements->item(0);

            if (!is_null($operationElement)) {
                $operation = self::getOperation($operationElement, $object);
            }

            if (!is_null($operation)) {
                $trigger->setOperation($operation);
            } else {
                $trigger = null ;
            }

        } else {
            $trigger = null ;
        }

        return $trigger ;
    }

    protected static function getOperation(
        \DOMElement     $operationElement,
        OperationObject $object=null
    )
    {

        $operation = null ;

        if (!is_null($operationElement)) {

            $operationType = $operationElement->getAttribute("type");

            switch (mb_strtoupper($operationType, 'utf-8')) {
            case "INSERT" :
                $operation = self::getInsertOperation(
                    $operationElement,
                    $object
                );
                break ;
            case "UPDATE" :
                $operation = self::getUpdateOperation(
                    $operationElement,
                    $object
                );
                break ;
            case "DELETE" :
                $operation = self::getDeleteOperation(
                    $operationElement,
                    $object
                );
                break ;
            case "SELECT" :
                $operation = self::getSelectOperation($operationElement);
                break ;
            }
        }

        return $operation ;
    }

    protected static function getSelectOperation(\DOMElement $operationElement)
    {
        $operation = new SelectOperation();

        $objectElements = $operationElement->getElementsByTagName('object');
        $mainObject = null ;
        foreach ($objectElements as $objectElement) {
            $object = new OperationObject(
                $objectElement->getAttribute('name'),
                $objectElement->getAttribute('alias')
            );
            if (is_null($mainObject)) {
                $mainObject = $object ;
            }
            $operation->addObject($object);
        }

        $columnElements = $operationElement->getElementsByTagName('column');
        foreach ($columnElements as $columnElement) {
            $object = $mainObject ;
            $name = $columnElement->getAttribute('name');

            $parsed = self::getParsedParameter($name);
            $name = $parsed['param'] ;
            if (!is_null($parsed['object'])) {
                $object = $operation->getObjectByAlias($parsed['object']);
            }

            $operation->addColumn(
                new OperationColumn(
                    $name,
                    $object,
                    $columnElement->getAttribute('alias')
                )
            );
        }

        $conditionElements = $operationElement->
            getElementsByTagName('condition');
        if (!is_null($conditionElements)) {
            $operation->setWhereClause(
                self::getClause($conditionElements->item(0), $operation)
            );
        }

        return $operation ;
    }

    protected static function getUpdateOperation(
        \DOMElement     $operationElement,
        OperationObject $object=null
    )
    {
        $operation = new UpdateOperation();

        $operationObject = new OperationObject(
            $operationElement->getAttribute("object")
        );
        $operation->addObject($operationObject);

        $params = $operationElement->getElementsByTagName("param");
        foreach ($params as $param) {
            if ($param->parentNode === $operationElement) {
                $operation->addClause(
                    self::getParamClause($param, $operation, $object)
                );
            }
        }

        $conditionElement = $operationElement->
            getElementsByTagName("condition")->item(0);
        $operation->setWhereClause(
            self::getClause($conditionElement, $operation, $object)
        );

        return $operation ;
    }

    protected static function getDeleteOperation(
        \DOMElement     $operationElement,
        OperationObject $object=null
    )
    {
        $operation = new DeleteOperation();

        $operationObject = new OperationObject(
            $operationElement->getAttribute("object")
        );
        $operation->addObject($operationObject);

        $conditionElement = $operationElement->
            getElementsByTagName("condition")->item(0);
        $operation->setWhereClause(
            self::getClause($conditionElement, $operation, $object)
        );

        return $operation ;
    }

    protected static function getInsertOperation(
        \DOMElement     $operationElement,
        OperationObject $object=null
    )
    {
        $operation = new InsertOperation();

        $operationObject = new OperationObject(
            $operationElement->getAttribute("object")
        );
        $operation->addObject($operationObject);

        $params = $operationElement->getElementsByTagName("param");
        foreach ($params as $param) {
            $operation->addClause(
                self::getParamClause($param, $operation, $object)
            );
        }

        return $operation ;
    }

    protected static function getClause(
        \DOMElement     $conditionElement,
        Operation       $operation=null,
        OperationObject $object=null
    )
    {
        $whereClause = new OperationMultipleClause("AND");

        $conditionParams = $conditionElement->getElementsByTagName("param");
        foreach ($conditionParams as $param) {
            if ($param->parentNode === $conditionElement) {
                $whereClause->addElement(
                    self::getParamClause($param, $operation, $object)
                );
            }
        }

        return $whereClause ;
    }

    protected static function getParamClause(
        \DOMElement     $param,
        Operation       $operation=null,
        OperationObject $obj=null
    )
    {

        $object = null ;
        $parsed = self::getParsedParameter($param->getAttribute("name"), $obj);
        $name = $parsed['param'] ;
        if (!is_null($parsed['object'])) {
            $object = $operation->getObjectByAlias($parsed['object']);
        } else {
            $object = $operation->getDefaultObject();
        }
        $column = new OperationColumn($name, $object);

        $object = null ;
        $parsed = self::getParsedParameter($param->getAttribute("value"), $obj);
        $val = $parsed['param'] ;
        if (!is_null($parsed['object'])) {
            if (!is_null($parsed['newOld'])) {
                $object = $obj ;
            } else {
                $object = $operation->getObjectByAlias($parsed['object']);
            }
        }

        if (is_null($object)) {
            $value = new OperationValue($val);
        } else {
            $value = new OperationColumn(
                $val, $object, null, $parsed['newOld']
            );
        }

        $clause = new OperationSimpleClause("=", $column, $value);

        return $clause ;
    }

    protected static function getParsedParameter(
        /*string*/      $param,
        OperationObject $superObject=null
    )
    {

        $parsed = array();
        $objectAlias = null ;
        $newOld = null ;

        $pointPos = strpos($param, '.');
        if ($pointPos>0) {
            $objectAlias = substr($param, 0, $pointPos);
            if (mb_strtoupper($objectAlias, 'utf-8') == 'NEW'
                || mb_strtoupper($objectAlias, 'utf-8') == 'OLD'
            ) {
                $newOld = mb_strtoupper($objectAlias, 'utf-8');

                $objectAlias = null ;
                if (!is_null($superObject)) {
                    $objectAlias = $superObject->getAlias();
                }
            }
            $param = substr($param, $pointPos+1);
        }

        $parsed['param'] = $param ;
        $parsed['object'] = $objectAlias ;
        $parsed['newOld'] = $newOld ;

        return $parsed ;
    }

    public static function getObjectParametersAsXml(
        ObjectParameters $objectParameters
    )
    {

        $doc = new \DOMDocument('1.0', 'UTF-8');
        $doc->formatOutput = true;

        $objectXml = $doc->createElement('object');
        $objectXml->setAttribute('name', $objectParameters->getName());
        $objectXml->setAttribute('class', $objectParameters->getClass());
        $objectXml->setAttribute('dao', $objectParameters->getDAO());

        $tableAtt = 'table' ;
        if ($objectParameters->isView()) {
            $tableAtt = 'view' ;
        }
        $objectXml->setAttribute($tableAtt, $objectParameters->getTable());

        $objectXml->setAttribute('schema', $objectParameters->getSchema());
        if ($objectParameters->getUseJoin()) {
            $objectXml->setAttribute('useJoin', 'true');
        }

        foreach ($objectParameters->getParameters() as $attribute) {

            $attributeXml = $doc->createElement('attribute');

            $attributeXml->setAttribute('name', $attribute->name);
            $attributeXml->setAttribute('column', $attribute->column);
            $attributeXml->setAttribute('type', $attribute->type);
            
            if (!isNullOrEmptyString($attribute->sqlType)
            ) {
                $attributeXml->setAttribute('sqlType', $attribute->sqlType);
            }
            if (!isNullOrEmptyString($attribute->length)
            ) {
                $attributeXml->setAttribute('length', $attribute->length);
            }

            if (!isNullOrEmptyString($attribute->foreignColumn)
            ) {
                $attributeXml->setAttribute(
                    'foreignColumn', $attribute->foreignColumn
                );
            }
            if (!isNullOrEmptyString($attribute->sequence)) {
                $attributeXml->setAttribute(
                    'sequence',
                    $attribute->sequence
                );
            }
            if (!isNullOrEmptyString($attribute->canBeNull)
                && $attribute->canBeNull===false
            ) {
                $attributeXml->setAttribute(
                    'nullable',
                    'false'
                );
            }
            if (!isNullOrEmptyString($attribute->primaryKey)
                && $attribute->primaryKey===true
            ) {
                $attributeXml->setAttribute('primaryKey', 'true');
            }
            if (!isNullOrEmptyString($attribute->unique)
                && $attribute->unique===true
            ) {
                $attributeXml->setAttribute('unique', 'true');
            }
            if (!isNullOrEmptyString($attribute->display)
                && $attribute->display===false
            ) {
                $attributeXml->setAttribute('display', 'false');
            }
            if (!isNullOrEmptyString($attribute->lazy)
                && $attribute->lazy===true
            ) {
                $attributeXml->setAttribute('lazy', 'true');
            }

            $objectXml->appendChild($attributeXml);
        }

        $constructionOperation = $objectParameters->getConstructionOperation();
        if (!is_null($constructionOperation)) {
            $constructionElement = $doc->createElement('construction');

            foreach ($constructionOperation->getObjects() as $object) {
                $objectElement = $doc->createElement('object');
                $objectElement->setAttribute('name', $object->getName());
                $objectElement->setAttribute('alias', $object->getAlias());

                $constructionElement->appendChild($objectElement);
            }

            foreach ($constructionOperation->getColumns() as $column) {
                $columnElement = $doc->createElement('column');
                
                $columnElement->setAttribute(
                    'name',
                    self::getColumnName($column)
                );
                $columnElement->setAttribute(
                    'alias',
                    $column->getAlias()
                );

                $constructionElement->appendChild($columnElement);
            }

            $constructionElement->appendChild(
                self::getWhereClauseElement(
                    $doc, $constructionOperation->getWhereClause()
                )
            );

            $objectXml->appendChild($constructionElement);
        }

        $triggers = $objectParameters->getTriggers();

        if (!is_null($triggers)) {
            $triggerElements = array();
            foreach ($triggers as $subTriggers) {

                foreach ($subTriggers as $trigger) {

                    $triggerElement = $doc->createElement('trigger');
                    $triggerElement->setAttribute('type', $trigger->getType());
                    $triggerElement->setAttribute('name', $trigger->getName());
                    $triggerElement->setAttribute('mode', $trigger->getMode());

                    $operation = $trigger->getOperation();
                    if (!is_null($operation)) {
                        $operationElement = $doc->createElement('action');
                        $operationElement->setAttribute(
                            'type',
                            $operation->getType()
                        );
                        $operationElement->setAttribute(
                            'object',
                            $operation->getObject(0)->getName()
                        );

                        if ($operation instanceof InsertOperation
                            || $operation instanceof UpdateOperation
                        ) {
                            $clauses = $operation->getClauses();
                            if (!is_null($clauses)) {
                                foreach ($clauses as $clause) {
                                    $operationElement->appendChild(
                                        self::getParamElement($doc, $clause)
                                    );
                                }
                            }
                        }

                        $whereClause = $operation->getWhereClause();
                        if (!is_null($whereClause)) {
                            $operationElement->appendChild(
                                self::getWhereClauseElement($doc, $whereClause)
                            );
                        }

                        $triggerElement->appendChild($operationElement);
                    }

                    $triggerElements[] = $triggerElement ;
                }
            }
            if (count($triggerElements)>0) {
                $triggersElement = $doc->createElement('triggers');
                foreach ($triggerElements as $triggerElement) {
                    $triggersElement->appendChild($triggerElement);
                }
                $objectXml->appendChild($triggersElement);
            }
        }

        $doc->appendChild($objectXml);

        return $doc ;
    }

    protected static function getColumnName(OperationColumn $column)
    {
        $columnName = $column->getName();
        if (!is_null($column->getObject())
            && !is_null($column->getObject()->getAlias())
        ) {
            $columnName = $column->getObject()->getAlias().'.'.$columnName ;
        }
        if (!is_null($column->getNewOld())
        ) {
            $columnName = $column->getNewOld() . '.' . $columnName ;
        }

        return $columnName ;
    }

    protected static function getWhereClauseElement(
        \DOMDocument    $doc,
        OperationClause $whereClause
    )
    {
        $conditionElement = $doc->createElement('condition');

        if ($whereClause instanceof OperationMultipleClause) {
            foreach ($whereClause->getElements() as $clause) {
                $conditionElement->appendChild(
                    self::getParamElement($doc, $clause)
                );
            }
        } else {
            $conditionElement->appendChild(
                self::getParamElement($doc, $whereClause)
            );
        }

        return $conditionElement ;
    }

    protected static function getParamElement($doc, $clause)
    {
        $paramElement = $doc->createElement('param');
        $paramElement->setAttribute(
            "name",
            self::getColumnName($clause->getColumn())
        );

        $value = "" ;
        if ($clause->getValue() instanceof OperationValue) {
            $value = $clause->getValue()->getValue();
        } else {
            $value = self::getColumnName($clause->getValue());
        }

        $paramElement->setAttribute("value", $value);
        return $paramElement ;
    }

    protected static function getMatchingForeignKey(
        ObjectParameters    $objectParam,
        AttributeParameters $attribute,
        /*string*/          $originObjectName=null 
    )
    {
        
        $fk = null ;
        $pkl = $objectParam->getPrimaryKey();
        if (count($pkl)==1) {
            $fk = $pkl[0];
        } else {
            foreach ($pkl as $cpk) {
                if ($cpk->column == $attribute->foreignColumn
                    || $cpk->column == $attribute->column
                ) {
                    $fk = $cpk ;
                    break;
                } else if ($cpk->type == $originObjectName) {
                    $fk = $cpk ;
                    break;
                }
            }
        }
        return $fk ;
    }

    public static function getSqlType(
        AttributeParameters $attribute,
        ObjectParameters    $objectParam,
        /*boolean*/         $ignoreSerial=false
    )
    {

        $type = null ;
        if ($attribute->isPrimitive() || $attribute->isPrimitiveObject()) {
            $sqlType = $attribute->sqlType ;
            if ($sqlType == SqlType::$serial && $ignoreSerial) {
                $sqlType = SqlType::$integer ;
            }
            $type = new SqlType(
                $sqlType,
                $attribute->length,
                $attribute->canBeNull
            );
        } else {
            $internalObjectParameters
                = ApplicationManager::getInstance()->
                                    getApplication()->
                                    getObjectParameter($attribute->type);
            $fk = self::getMatchingForeignKey(
                $internalObjectParameters, $attribute, $objectParam->getName()
            );
            if ($fk->type == $objectParam->getName()) {
                $fk = self::getMatchingForeignKey($objectParam, $attribute);
            }
            $fk->canBeNull = $attribute->canBeNull;
            $type = self::getSqlType($fk, $internalObjectParameters, true);
        }
        return $type ;
    }

    public static function getObjectParametersAsDeleteSqlQuery(
        ObjectParameters $objectParameters
    )
    {
        $table = new SqlTable(
            $objectParameters->getTable(),
            $objectParameters->getSchema()
        );

        if (!$objectParameters->isView()) {
            $query = new SqlDeleteTableQuery();
        } else {
            $query = new SqlDeleteViewQuery();
        }
        $query->setTable($table);

        return $query ;
    }

    public static function getObjectParametersAsSqlQuery(
        ObjectParameters $objectParameters
    )
    {
        if ($objectParameters->isView()) {
            return self::getObjectParametersAsCreateViewSqlQuery(
                $objectParameters
            );
        } else {
            return self::getObjectParametersAsCreateTableSqlQuery(
                $objectParameters
            );
        }
    }

    protected static function getObjectParametersAsCreateViewSqlQuery(
        ObjectParameters $objectParameters
    )
    {

        $queries = array();

        $table = new SqlTable(
            $objectParameters->getTable(),
            $objectParameters->getSchema()
        );

        $query = new SqlCreateViewQuery();
        $query->setTable($table);

        $constructionOperation = $objectParameters->getConstructionOperation();
        if (!is_null($constructionOperation)
            && $constructionOperation instanceof SelectOperation
        ) {
            $selectQuery = self::getSelectSqlQueryFromSelectOperation(
                $constructionOperation
            );
            $query->setSelectQuery($selectQuery);
        }

        $queries[] = $query ;

        foreach ($objectParameters->getTriggers() as $type=>$triggers) {
            foreach ($triggers as $trigger) {
                $queries[]
                    = self::getTriggerAsCreateTriggerSqlQuery(
                        $trigger,
                        $objectParameters
                    );
            }
        }

        return $queries ;
    }

    public static function getTriggerAsCreateTriggerSqlQuery(
        Trigger $trigger,
        ObjectParameters $superObjectParameter,
        Object $object=null
    )
    {

        $query = new SqlCreateTriggerQuery();

        $query->setName($trigger->getName());

        $triggerType = null ;
        switch(mb_strtoupper($trigger->getType(), 'utf-8')) {
        case "INSERT" :
            $triggerType = SqlQuery::$insert ;
            break;
        case "UPDATE" :
            $triggerType = SqlQuery::$update ;
            break;
        case "DELETE" :
            $triggerType = SqlQuery::$delete ;
            break;
        }
        $query->setTriggerType($triggerType);

        $query->setMode($trigger->getMode());

        $resps = self::getTableFromObject($trigger->getObject());
        $query->setTable($resps['table']);

        $query->setAction(
            self::getSqlQueryFromOperation(
                $trigger->getOperation(),
                $superObjectParameter,
                $object
            )
        );

        return $query ;
    }

    protected static function getTableFromObject(OperationObject $object)
    {

        $objectParameters = null ;

        if (array_key_exists($object->getName(), self::$objectParameters)) {
            $objectParameters = self::$objectParameters[$object->getName()] ;
        } else {
            $applicationManager = ApplicationManager::getInstance();
            $application = $applicationManager->getApplication();

            $objectParameters
                = $application->getObjectParameter($object->getName());
        }

        $table = new SqlTable(
            $objectParameters->getTable(),
            $objectParameters->getSchema(),
            $object->getAlias()
        );

        return array('objectParameters'=>$objectParameters, 'table'=>$table);
    }

    protected static function getSqlColumnFromOperationColumn(
        OperationColumn  $column,
        /*array*/        $tables,
        /*array*/        $objectParametersArray,
        ObjectParameters $superObjectParameter=null
    )
    {
        $sqlColumn = null ;
        if (is_null($column)) {
            return null ;
        }

        if (is_null($column->getNewOld())) {
            $table = $tables[$column->getObject()->getAlias()] ;
            $objectParameters
                = $objectParametersArray[$column->getObject()->getAlias()] ;
            $colParam = $objectParameters->getParameter($column->getName());
            if (!is_null($colParam)) {
                $sqlColumn = new SqlColumn($colParam->column, $table, null);
            }
        } else {
            $columnName = $column->getName();
            if (!is_null($superObjectParameter)) {
                $columnName
                    = $superObjectParameter
                        ->getParameter($column->getName())
                        ->column ;
            }
            $sqlColumn = new SqlColumn($columnName);
            $sqlColumn->setNewOld($column->getNewOld());
        }

        return $sqlColumn ;
    }

    protected static function getSqlClauseFromOperationClause(
        OperationClause $clause,
        $tables,
        $objectParametersArray,
        ObjectParameters $superObjectParameter=null,
        Object $object=null
    )
    {

        $sqlOperation = null ;

        if ($clause instanceof OperationMultipleClause) {
            $sqlOperation = new SqlOperation(SqlOperation::$and);
            foreach ($clause->getElements() as $element) {
                if ($element instanceof OperationSimpleClause) {
                    $op = self::getSqlClauseFromOperationClause(
                        $element,
                        $tables,
                        $objectParametersArray,
                        $superObjectParameter,
                        $object
                    );
                    $sqlOperation->addParameter($op);
                }
            }
        } else {
            $sqlOperation = new SqlOperation(SqlOperation::$equal);

            $column = $clause->getColumn();
            $sqlColumn = self::getSqlColumnFromOperationColumn(
                $column,
                $tables,
                $objectParametersArray,
                $superObjectParameter
            );

            $val = $clause->getValue();
            $value = null ;
            if ($val instanceof OperationValue) {
                $value = new SqlValue($val->getValue());
            } else {
                if (!is_null($val->getNewOld())
                    && !is_null($superObjectParameter)
                    && !is_null($object)
                ) {
                    $getter
                        = $superObjectParameter
                            ->getParameter($val->getName())
                            ->getter ;

                    $objectValue = $object->{$getter}();
                    if ($objectValue instanceof Object) {
                        $objectValue = $objectValue->getId();
                    }

                    $value = new SqlValue($objectValue);
                } else {
                    $value = self::getSqlColumnFromOperationColumn(
                        $val,
                        $tables,
                        $objectParametersArray,
                        $superObjectParameter
                    );
                }
            }

            $sqlOperation->addParameter($sqlColumn);
            $sqlOperation->addParameter($value);
        }

        return $sqlOperation ;
    }

    public static function getSqlQueryFromOperation(
        Operation $operation,
        ObjectParameters $superObjectParameter=null,
        Object $object=null
    )
    {
        if ($operation instanceof SelectOperation) {
            return self::getSelectSqlQueryFromSelectOperation(
                $operation
            );
        } else if ($operation instanceof InsertOperation) {
            return self::getInsertSqlQueryFromInsertOperation(
                $operation,
                $superObjectParameter,
                $object
            );
        } else if ($operation instanceof UpdateOperation) {
            return self::getUpdateSqlQueryFromUpdateOperation(
                $operation,
                $superObjectParameter,
                $object
            );
        } else if ($operation instanceof DeleteOperation) {
            return self::getDeleteSqlQueryFromDeleteOperation(
                $operation,
                $superObjectParameter,
                $object
            );
        }
    }

    protected static function getSelectSqlQueryFromSelectOperation(
        SelectOperation $operation
    )
    {
        $firstTable = true ;

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createSelectQuery();

        $tables = array();
        $objectParametersArray = array();
        $objects = $operation->getObjects();
        if (!is_null($objects)) {

            $applicationManager = ApplicationManager::getInstance();
            $application = $applicationManager->getApplication();

            foreach ($objects as $object) {

                $resp = self::getTableFromObject($object);
                $objectParameter = $resp['objectParameters'] ;
                $table = $resp['table'] ;

                $tables[$table->getAlias()] = $table ;
                $objectParametersArray[$table->getAlias()] = $objectParameter ;

                if ($firstTable) {
                    $queryBuilder->setTable($table);
                    $firstTable = false ;
                } else {
                    $queryBuilder->addJoin(new SqlJoin($table));
                }
            }
        }

        $columns = array();
        $cols = $operation->getColumns();
        if (!is_null($cols)) {
            foreach ($cols as $column) {
                $table = $tables[$column->getObject()->getAlias()] ;
                $objectParameters
                    = $objectParametersArray[$column->getObject()->getAlias()] ;
                $colParam = $objectParameters->getParameter($column->getName());
                if (!is_null($colParam)) {
                    $sqlColumn = new SqlColumn(
                        $colParam->column,
                        $table,
                        null,
                        $column->getAlias()
                    );
                    $queryBuilder->addSelectColumn($sqlColumn);
                }
            }
        }

        $whereClause = $operation->getWhereClause();
        if (!is_null($whereClause)) {
            $sqlOperation = self::getSqlClauseFromOperationClause(
                $whereClause,
                $tables,
                $objectParametersArray,
                null,
                null
            );
            if (!is_null($sqlOperation)) {
                $queryBuilder->addWhereClause($sqlOperation);
            }
        }

        return $queryBuilder->buildQuery();
    }

    protected static function getInsertSqlQueryFromInsertOperation(
        InsertOperation $operation,
        ObjectParameters $superObjectParameter=null,
        Object $obj=null
    )
    {

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createInsertQuery();

        $object = $operation->getObject(0);
        $objectParameter = null ;
        $table = null ;

        if (!is_null($object)) {
            $resp = self::getTableFromObject($object);
            $objectParameter = $resp['objectParameters'] ;
            $table = $resp['table'] ;
            $queryBuilder->setTable($table);
        }

        $clauses = $operation->getClauses();
        if (!is_null($clauses)) {
            foreach ($clauses as $clause) {
                $queryBuilder->addParameter(
                    self::getSqlClauseFromOperationClause(
                        $clause,
                        array(''=>$table),
                        array(''=>$objectParameter),
                        $superObjectParameter,
                        $obj
                    )
                );
            }
        }

        return $queryBuilder->buildQuery();
    }

    protected static function getUpdateSqlQueryFromUpdateOperation(
        UpdateOperation $operation,
        ObjectParameters $superObjectParameter=null,
        Object $obj=null
    )
    {

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createUpdateQuery();

        $object = $operation->getObject(0);
        $objectParameter = null ;
        $table = null ;

        if (!is_null($object)) {
            $resp = self::getTableFromObject($object);
            $objectParameter = $resp['objectParameters'] ;
            $table = $resp['table'] ;
            $queryBuilder->setTable($table);
        }

        $tables = array(''=>$table);
        $objectParametersArray = array(''=>$objectParameter);

        $clauses = $operation->getClauses();
        if (!is_null($clauses)) {
            foreach ($clauses as $clause) {
                $queryBuilder->addParameter(
                    self::getSqlClauseFromOperationClause(
                        $clause,
                        $tables,
                        $objectParametersArray,
                        $superObjectParameter,
                        $obj
                    )
                );
            }
        }

        $whereClause = $operation->getWhereClause();
        if (!is_null($whereClause)) {
            $sqlOperation = self::getSqlClauseFromOperationClause(
                $whereClause,
                $tables,
                $objectParametersArray,
                $superObjectParameter,
                $obj
            );
            if (!is_null($sqlOperation)) {
                $queryBuilder->addWhereClause($sqlOperation);
            }
        }

        return $queryBuilder->buildQuery();
    }

    protected static function getDeleteSqlQueryFromDeleteOperation(
        DeleteOperation  $operation,
        ObjectParameters $superObjectParameter=null,
        Object           $obj=null
    )
    {

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createDeleteQuery();

        $object = $operation->getObject(0);
        $objectParameter = null ;
        $table = null ;

        if (!is_null($object)) {
            $resp = self::getTableFromObject($object);
            $objectParameter = $resp['objectParameters'] ;
            $table = $resp['table'] ;
            $queryBuilder->setTable($table);
        }

        $tables = array(''=>$table);
        $objectParametersArray = array(''=>$objectParameter);

        $whereClause = $operation->getWhereClause();
        if (!is_null($whereClause)) {
            $sqlOperation = self::getSqlClauseFromOperationClause(
                $whereClause,
                $tables,
                $objectParametersArray,
                $superObjectParameter,
                $obj
            );
            if (!is_null($sqlOperation)) {
                $queryBuilder->addWhereClause($sqlOperation);
            }
        }

        return $queryBuilder->buildQuery();
    }

    protected static function getObjectParametersAsCreateTableSqlQuery(
        ObjectParameters $objectParameters
    )
    {
        $table = new SqlTable(
            $objectParameters->getTable(),
            $objectParameters->getSchema()
        );

        $query = new SqlCreateTableQuery();
        $query->setTable($table);

        $primaryKey = $objectParameters->getPrimaryKey();
        if (!is_null($primaryKey) && count($primaryKey)>0) {
            $pkAttributes = array();
            foreach ($primaryKey as $attribute) {
                $column = new SqlColumn($attribute->column, $table);
                $pkAttributes[] = $column ;
            }
            $query->addIndex(
                new SqlPrimaryKey($pkAttributes, $table->getName() . '_pk')
            );
        }

        foreach ($objectParameters->getParameters() as $attribute) {
            /* @var $attribute AttributeParameters */
            if (!$attribute->isArray) {
                $type = null ;
                $isfk = false ;
                if ($attribute->isPrimitive() || $attribute->isPrimitiveObject()) {
                    $st = $attribute->sqlType ;
                    if ($st == SqlType::$integer
                        && !isNullOrEmptyString($attribute->sequence)
                    ) {
                        $st = SqlType::$serial ;
                    }
                    $type = new SqlType(
                        $st,
                        $attribute->length,
                        $attribute->canBeNull
                    );
                } else {
                    $type = self::getSqlType($attribute, $objectParameters);
                    $isfk = true ;
                }
                
                $column = new SqlColumn($attribute->column, $table, $type);
                $query->addParameter($column);
                
                if ($isfk) {
                    $internalObjectParameters
                        = ApplicationManager::getInstance()->
                                            getApplication()->
                                            getObjectParameter(
                                                $attribute->type
                                            );
                    $foreignTable
                        = new SqlTable(
                            $internalObjectParameters->getTable(),
                            $internalObjectParameters->getSchema()
                        );
    
                    $fk = self::getMatchingForeignKey(
                        $internalObjectParameters,
                        $attribute,
                        $objectParameters->getName()
                    );
                    $query->addIndex(
                        new SqlForeignKey(
                            $column,
                            new SqlColumn($fk->column, $foreignTable),
                            $table->getName().'_'.$attribute->column.'_'.
                            $foreignTable->getName().'_fk'
                        )
                    );
                }
            }
        }
        
        return $query ;
    }

    public static function getInstance()
    {

        if (is_null(self::$_singleton)) {
            self::$_singleton = new ObjectParametersDAO();
        }
        return self::$_singleton ;
    }

}
